/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufpr.inf.escel.testing.tools;

import br.ufpr.inf.escel.brain.model.Individual;
import br.ufpr.inf.escel.brain.model.pp.DadoTestePP;
import br.ufpr.inf.escel.consts.Constantes;
import br.ufpr.inf.escel.testing.Console;
import br.ufpr.inf.escel.testing.model.Cut;
import br.ufpr.inf.escel.testing.model.CutPP;
import br.ufpr.inf.escel.testing.tools.impl.Ferramenta;
import br.ufpr.inf.escel.manager.util.Arquivo;
import br.ufpr.inf.escel.util.Writer;

/**
 * OBS: A PROTEUM SÓ PODERÁ SER EXECUTADA APÓS A POKETOOL.
 * Isso se deve porque a poketool escreve os dados de teste
 * em disco, e em seguida, a proteum lê esses dados
 * @author gustavo
 */
public class Proteum extends Ferramenta {

    private Console bash;
    private CutPP cut;
    private boolean hasExecutavel;
    private boolean hasInitialized;

    public Proteum() {
        bash = Console.getInstance();
        hasExecutavel = false;
        hasInitialized = false;
    }

    public double run(Individual ind) {
        Writer.it("Executing proteum test cases. ind" + ind.getId());

        if (Constantes.FITNESS_DADO_TESTE.equals("PROTEUM")) {
            for (int numTest = 0; numTest < ind.getCapacidade(); numTest++) {
                DadoTestePP dadoPP = (DadoTestePP) ind.getDadoTeste(numTest);

                gerarMutantes(ind.getId(), numTest, numTest);
                executarMutantes(numTest, numTest);
                dadoPP.setFitness(avaliarDadoTeste());
                ind.update(ind.getDadoTeste(numTest), dadoPP);
            }
        }
        inicializarProteum();
        gerarMutantes(ind.getId(), 1, ind.getCapacidade());
        //executando de 0 até 0 é o mesmo que todos na proteum
        executarMutantes(1, ind.getCapacidade());
        double score = avaliarDadoTeste();
        return score;
    }

    public void inicializarProteum() {
        gerarExecutavel();

        if (!hasInitialized) {

            String[] cmd = {
                "cd " + cut.getNovoPath(),
                pteste(),
                li(),
                "li2nli __" + cut.basename()
            };
            bash.run(cmd);
            hasInitialized = true;
        }
    }

    public void gerarMutantes(int numIndividual, int numTesteInicio, int numTesteFim) {
        String[] cmd = {
            "cd " + cut.getNovoPath(),
            "tcase -i sessao1",
            "tcase -create sessao1",
            "muta -create sessao1",
//            "muta-gen -all 100 sessao1",
            "muta-gen -all 0 -SWDD 100 -SMTC 100 -SSDL 100 -OLBN 100 -ORRN 100 -VTWD 100 -VDTR 100 -Cccr 100 -Ccsr 100 sessao1",
            tcase(numIndividual, numTesteInicio, numTesteFim)
        };
        bash.run(cmd);
    }

    public Long executarMutantes(int dadoInicio, int dadoFim) {
        String[] cmd = {
            "cd " + cut.getNovoPath(),
            "tcase -e sessao1",
            //            "tcase -e -f " + dadoInicio + " -t " + dadoFim + " sessao1",
            "exemuta -exec sessao1",
            "report -tcase -L 510 sessao1"
        };

        long init = System.currentTimeMillis();
        bash.run(cmd);
        return System.currentTimeMillis() - init;
    }

    private void gerarExecutavel() {
        if (!hasExecutavel) {

            String[] gerarExecutavel = {
                "cd " + cut.getNovoPath(),
                cut.gerarExecutavel("proteum")
            };

            bash.run(gerarExecutavel);
            hasExecutavel = true;
        }
    }

    /**
     * ptest - creates and handles program test les
     */
    private String pteste() {
        return "pteste -create -S " + cut.basename() + " -E exec-proteum -C \"" + cut.gerarExecutavel("proteum") + "\" sessao1";
    }

    /**
     * Formato da linguagem intermediária:
     *  li -P __basename basename __basename
     * @param cut
     * @return string formatada
     */
    private String li() {
        StringBuffer li = new StringBuffer(Constantes.PASTA_PROTEUM);
        li.append("/li -P").append(" __").append(cut.basename());
        li.append(" ").append(cut.basename());
        li.append(" __").append(cut.basename());
        return li.toString();
    }

    /**
     * tcase para importação de dados de teste de programas
     * que aceitam parametros. Formato do comando:
     * tcase -add -E exec-proteum -p tcase/indx/casey sessao1
     * 
     * @return comado tcase
     */
    private String tcaseParam(int numIndividuo, int numTestInicio, int numTestFim) {
        StringBuffer tcase = new StringBuffer();

        for (int i = numTestInicio; i < numTestFim; i++) {
            tcase.append("tcase -add -E ").append(cut.getExecutavel());
            tcase.append(" -p '$(cat tcase/ind").append(numIndividuo).append("/case").append(i).append(")' sessao1\n");
        }

        return tcase.toString();
    }

    /**
     * Importar dado de teste. Formato do comando:
     * tcase -ascii -E exec-proteum -DD tcase/ind2/ -I case -f 4 -t 4 sessao1
     * @param cut
     * @param numIndividuo
     * @param numDadoTeste
     * @return comando formatado
     */
    private String tcase(int numIndividuo, int numTestInicio, int numTestFim) {
        StringBuffer tcase = new StringBuffer("tcase -ascii -E ");
        tcase.append(cut.getExecutavel());
        tcase.append(" -DD ").append(cut.getNovoPath());
        tcase.append("tcase/ind").append(numIndividuo).append(" -I case -f ");
        tcase.append(numTestInicio).append(" -t ").append(numTestFim);
        tcase.append(" sessao1");

        return tcase.toString();
    }

    /**
     * Retira do arquivo de relatório (sessao1.lst) a informação
     * sobre o score de mutantes mortos pelo dado de teste
     * @param cut
     * @return score do dado de teste
     */
    public double avaliarDadoTeste() {
        String report = Arquivo.leia(cut.getNovoPath() + "sessao1.lst");

        report = report.substring(report.indexOf("SCORE"));
        report = report.substring(9, report.indexOf("["));

        return Double.parseDouble(report) * 100;
    }

    public void setCut(Cut cut) {
        this.cut = (CutPP) cut;
        bash.setNovoDirCut(cut.getNovoPath());
    }
}
